package com.jsg.sl651.server;

import com.jsg.sl651.boot.BootProperties;
import com.jsg.sl651.codec.FrameDecoder;
import com.jsg.sl651.codec.FrameEncoder;
import com.jsg.sl651.codec.HeaderDelimiterFrameDecoder;
import com.jsg.sl651.entity.frame.FrameMessageWrapper;
import com.jsg.sl651.handler.MessageHandler;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.handler.logging.LoggingHandler;
import io.netty.util.ReferenceCountUtil;
import io.netty.util.ResourceLeakDetector;
import io.netty.util.concurrent.EventExecutorGroup;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.PostConstruct;
import java.util.List;

import static com.sun.media.jfxmediaimpl.HostUtils.isLinux;

@AllArgsConstructor
@Slf4j
public class SL651Server {

    private final BootProperties bootProperties;
    private final FrameDecoder frameDecoder;
    private final FrameEncoder frameEncoder;
    private final EventLoopGroup bossGroup;
    private final EventLoopGroup workerGroup;
    private final EventExecutorGroup businessGroup;
    private final MessageHandler messageHandler;

    @PostConstruct
    public void init() {
        if (bootProperties.isEnable()) {
            log.info("[初始化]端口号：{}", bootProperties.getPort());
            try {
                start();
            } catch (Exception e) {
                log.error("[初始化]端口号：{}", bootProperties.getPort(), e);
                e.printStackTrace();
            }
        } else {
            log.info("[阻断初始化]端口号：{}", bootProperties.getPort());
        }
    }

    /**
     * 启动 SL651 服务器。
     * <p>
     * 该方法配置并启动 Netty 服务器，设置通道、选项、日志处理程序、子通道处理程序等。
     * 子通道处理程序中添加了帧解码器、帧编码器和消息处理器。
     * </p>
     *
     * @throws InterruptedException 如果在启动过程中发生中断，则抛出此异常
     */
    public void start() throws InterruptedException {
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workerGroup)
                .channel(isLinux() && bootProperties.isLinuxUseEpoll() ? EpollServerSocketChannel.class : NioServerSocketChannel.class)
                //服务端可连接队列数,对应TCP/IP协议listen函数中backlog参数
                .option(ChannelOption.SO_BACKLOG, bootProperties.getBacklog())
                // 日志登记
                .handler(new LoggingHandler(bootProperties.getLogLevel()))
                .childOption(ChannelOption.SO_KEEPALIVE, bootProperties.isKeepAlive())
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        ChannelPipeline ch = socketChannel.pipeline();
                        if (bootProperties.isHeaderDelimiter()) {
                            ch.addLast(new HeaderDelimiterFrameDecoder(1024, bootProperties.getServerCodeType()));
                        }
                        // 解码器
                        ch.addLast(new ByteToMessageDecoder() {
                            @Override
                            protected void decode(ChannelHandlerContext channelHandlerContext, ByteBuf byteBuf, List<Object> list) throws Exception {
                                FrameMessageWrapper decode = frameDecoder.decode(byteBuf, channelHandlerContext);
                                if (decode != null) {
                                    list.add(decode);
                                }
                            }
                        });
                        // 编码器
                        ch.addLast(new MessageToByteEncoder<FrameMessageWrapper>() {
                            @Override
                            protected void encode(ChannelHandlerContext channelHandlerContext, FrameMessageWrapper frameMessageWrapper, ByteBuf byteBuf) throws Exception {
                                ByteBuf encode = frameEncoder.encode(frameMessageWrapper);
                                if (encode != null) {
                                    byteBuf.writeBytes(encode);
                                    ReferenceCountUtil.safeRelease(encode);
                                }
                            }
                        });
                        // 逻辑处理器
                        ch.addLast(businessGroup, messageHandler);
                    }
                });
        //内存泄漏检测
        ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.SIMPLE);
        ChannelFuture bind = serverBootstrap.bind(bootProperties.getPort());
        bind.sync().addListener(future -> {
            if (future.isSuccess()) {
                log.info("[启动成功]端口号：{}", bootProperties.getPort());
            }
        }).channel().closeFuture().addListener(future -> {
            log.info("[关闭成功]端口号：{}", bootProperties.getPort());
            bossGroup.shutdownGracefully().syncUninterruptibly();
            workerGroup.shutdownGracefully().syncUninterruptibly();
            businessGroup.shutdownGracefully().syncUninterruptibly();
        });
    }
}
